<?php

namespace App\Model;

use App\Services\AliPayService;
use App\Services\WechatService;
use Carbon\Carbon;
use Hyperf\Utils\Arr;

/**
 * App\PaymentOrder
 *
 * @property int $id
 * @property int $user_id 用户
 * @property int $fee 金额
 * @property \App\Model\User $user 用户模型
 * @property int $refunded_fee 退款金额
 * @property string $number 订单号
 * @property string $type 类型
 * @property \Carbon\Carbon|null $paid_at 支付时间
 * @property string|null $description 支付说明
 * @property string $payment_type 支付方式
 * @property array|null $extra 其他信息
 * @property int|null $target_id 支付订单指向的目标id  如订单id
 */
class PaymentOrder extends Model
{
    public bool $timestamps = true;
    protected ?string $table = 'payment_orders';


    const USER_SOURCE_TO_PAYMENT_TYPE = [
        User::SOURCE_ALI_PAY => Bill::PAYMENT_TYPE_ALI_PAY,
        User::SOURCE_WE_CHAT => Bill::PAYMENT_TYPE_WE_CHAT_PAY,
    ];


    protected array $fillable = [
        'type', 'number', 'payment_type',
        'extra', 'target_id','status','out_ord_id','user_id',
        'fee','paid_at','refunded_fee','payment_id'
    ];

    protected array $casts = [
        'extra' => 'json',
        'paid_at' => 'datetime',
    ];

    public function getTypeDes()
    {
        return Arr::get(Bill::TYPE_MAP, $this->type);
    }

    public function scopeIsPaid($query)
    {
        return $query->whereNotNull('paid_at');
    }

    public function paySuccess()
    {
        $this->paid_at = Carbon::now();
        $extra = $this->extra;

        $user = $this->user;

        $fee = $this->fee;

        switch ($this->type)
        {
            //余额充值
            case Bill::TYPE_CHARGE_BALANCE:
                $description = null;
                $field = 'balance';
                if(Arr::get($extra, 'use_give') && Arr::get($extra, 'device_sn')) {
                    $sn = Arr::get($extra, 'device_sn');
                    //小于等于当前充值金额的且金额最大的一条充值送的规则
                    $chargeBalanceGive = ChargeBalanceGive::isActive()->with('coupon')->where('charge_fee', '<=', $fee)
                        ->whereHas('devices', function($q) use ($sn) {
                            $q->where('sn', $sn);
                        })
                        ->orderByDesc("charge_fee")->first();

                    if($chargeBalanceGive) {
                        $giveFee = $chargeBalanceGive->give_fee;

                        if ($giveFee > 0) {
                            $description = '充' . $chargeBalanceGive->charge_fee . '送' . $chargeBalanceGive->give_fee;
                            $field = 'frozen_balance';
                            //赠送的钱 加钱记账 支付方式为 系统划账
                            $user->increment($field, $giveFee);
                            Bill::record(
                                $user,
                                $giveFee,
                                Bill::TYPE_CHARGE_BALANCE_GIVE,
                                Bill::PAYMENT_TYPE_SYSTEM,
                                $chargeBalanceGive->id,
                                $extra,
                                $description
                            );
                        }

                        if ($chargeBalanceGive->coupon_id) {
                            $device = Device::findOrFailViaSn($sn);
                            for($i = 1; $i <= $chargeBalanceGive->quantity; $i ++) {
                                $couponRecords = new CouponRecord([
                                    'expires_at' => Carbon::now()->addDays($chargeBalanceGive->coupon->days),
                                ]);
                                $couponRecords->coupon()->associate($chargeBalanceGive->coupon);
                                $couponRecords->user()->associate($user);
                                $couponRecords->device()->associate($device);
                                $couponRecords->station()->associate($device->station_id);
                                $couponRecords->save();
                            }
                        }
                    }
                }

                //充值的本钱 加钱记账
                $user->increment($field, $fee);
                Bill::record(
                    $user,
                    $fee,
                    $this->type,
                    $this->payment_type,
                    null,
                    $extra,
                    $description
                );

                break;

            case Bill::TYPE_ORDER_PAY:
                Bill::record(
                    $user,
                    $fee,
                    $this->type,
                    $this->payment_type,
                    $this->target_id
                );

                $order = Order::findOrFail($this->target_id);
                $order->paySuccess();
                break;

                //买断续费
            case Bill::TYPE_DISTRIBUTOR_DEVICE_RENEW:
                $distributorId = Arr::get($extra, 'distributor_id');

                $distributor = Distributor::findOrFail($distributorId);

                Bill::record(
                    $distributor,
                    - $fee,
                    $this->type,
                    $this->payment_type,
                    $this->target_id
                );

                $device = Device::findOrFail($this->target_id);
                $device->purchaseRenew();

            default :
                // others ...
        }

        $this->extra = $extra;

        $this->save();
    }

//    public function checkIsPaid()
//    {
//        return (bool) $this->paid_at;
//    }
//
//    public function scopeIsPaid($query)
//    {
//        return $query->whereNotNull('paid_at');
//    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

//    //访问器  修改description
//    public function getDescriptionAttribute($value)
//    {
//        return $value ?? $this->getTypeDes();
//    }

    /**
     * @return array
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     * 获取支付配置
     */
    public function getPaymentConfig()
    {
        $user = $this->user;

        $result = [
            'payment_order_id' => $this->id,
            'need_pay' => true,
        ];

        $fee = $this->fee;

        if(app()->isLocal() || $fee <= 0) {
            //本地就直接操作为支付成功
            $this->paySuccess();
            $result['need_pay'] = false;
            $result['payment_config'] = [
                'message' => 'not need pay'
            ];
        } else {


            switch ($this->payment_type)
            {
                case Bill::PAYMENT_TYPE_WE_CHAT_PAY:
                    $payment  = WechatService::getPayment();

                    $res = $payment->order->unify([
                        'body' => $this->description ?? $this->getTypeDes(),
                        'out_trade_no' => $this->number,
                        'total_fee' => (int) ($fee * 100),
                        'trade_type' => 'JSAPI',
                        'openid' => $user->openid,
                    ]);


                    self::checkWeChatResultOrFail($res);

                    //根据下单的id 获取支付所需的配置
                    $config = $payment->jssdk->bridgeConfig($res['prepay_id'], false);
                    $config['timestamp'] = Arr::get($config, 'timestamp', Arr::get($config, 'timeStamp'));

                    break;

                case Bill::PAYMENT_TYPE_ALI_PAY;
                    //支付宝支付

                    $tradeApi = AliPayService::getTradeApi();

                    $config = $tradeApi->wapPay(Arr::get($this->extra, 'return_url'), [
                        'out_trade_no' => $this->number,
                        'total_amount' => $this->fee,
                        'subject' => $this->getTypeDes(),
                        //可选
//                        'body' => '余额充值',
//                        'quit_url' => config('app.url'),
                        //....
                    ]);

                    break;

                default:
                    $config = [];
                    abort(400, 'invalid payment type');
            }

            $result['payment_config'] = $config;
        }

        return $result;
    }


//
//    /**
//     * @param integer $refundFee
//     * @param string $description
//     * @param array $extra
//     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
//     * 退款 并记录退款单
//     */
//    public function refundAndRecord($refundFee = null, $description = '退款', $extra = [])
//    {
//        $refundFee = abs($refundFee ?? $this->fee);
//
//        //根据支付平台退款
//        $this->outPaymentRefund($refundFee, $description);
//
//        $user = $this->user;
//
//        //加入支付订单的id
//        $extra['payment_order_id'] = $this->id;
//
//
//        //记账
//        $bill = new Bill([
//            'fee' => - $refundFee,
//            'type' => Bill::TYPE_REFUND,
//            'payment_type' => Bill::PAYMENT_TYPE_SYSTEM,
//            'description' => $description,
//            'extra' => $extra,
//        ]);
//        $bill->account()->associate($user);
//        $bill->save();
//    }


    /**
     * @param int $refundFee
     * @param string $des
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * 外部付款退回
     */
    public function outPaymentRefund($refundFee, $des)
    {
        $user = $this->user;

        $fee = $this->fee;

        if($refundFee > $fee - $this->refunded_fee) {
            abort(400, "退款金额超出该支付订单可退款金额 {$this->id} refund fee:" . $refundFee);
        }

        //非本地才退款
        if(!app()->isLocal()) {

            switch ($this->payment_type)
            {
                //用微信支付的
                case Bill::PAYMENT_TYPE_WE_CHAT_PAY:
                    $payment  = WechatService::getPayment();

                    logger('refund params', [
                        $this->number, rand_number($user->id), (int) ($fee * 100), $refundFee, ['refund_desc' => $des]
                    ]);

                    // 参数分别为：商户订单号、商户退款单号、订单金额、退款金额(分)、其他参数
                    $res = $payment->refund->byOutTradeNumber(
                        $this->number,
                        rand_number($user->id),
                        (int) ($fee * 100), (int) ($refundFee * 100),
                        ['refund_desc' => $des]
                    );

                    $this->checkWeChatResultOrFail($res);

                    break;
                //用支付支付的
                case Bill::PAYMENT_TYPE_ALI_PAY:
                    $tradeApi  = AliPayService::getTradeApi();

                    $tradeApi->refund([
                        'out_trade_no' => $this->number,
                        'refund_amount' => $fee,
                        'out_request_no' => rand_number($this->id),
                        'refund_reason' => $des,
                    ]);
                    break;
            }
        }

        //递增退款了的金额
        $this->increment('refunded_fee', $refundFee);

    }


    /**
     * @param $res
     * 校验微信返回结果  异常则抛出对应的错误信息
     */
    public static function checkWeChatResultOrFail($res)
    {
        logger('we chat res ', $res);

        if(Arr::get($res, 'return_code') != 'SUCCESS') {
            abort(400, '请求失败,  msg: ' . Arr::get($res, 'return_msg'));
        }

        if(Arr::get($res, 'result_code') != 'SUCCESS') {
            abort(400, '请求失败,  msg: ' . Arr::get($res, 'err_code_des'));
        }

    }

}
